﻿using System;
using System.Collections.Generic;
using SoftwareConsulting.BI11.NumericalMethods.Geometry;
using SoftwareConsulting.BI11.Studio.InternalServices;

namespace SoftwareConsulting.BI11.NumericalMethods.Classification
{    
    /// <summary>
    /// Класс модифицированной классификации (без групп признаков, но зато расстояния можно искать различными способами)
    /// </summary>
    public class ModifiedClassificator: AbstractClassificator
    {        
        private DistanceMethod _method; //метод нахождения расстояний
        private double _EMaximumDifference;//коэффициент E - максимально допустимое расстояние между экземпляром из выборки примеров и рассматриваемым экземпляром
        private int[] _attributesWeights;//веса признаков

        /// <summary>
        /// Конструктор класса
        /// </summary>
        public ModifiedClassificator() :
            base()
        {            
        }

        /// <summary>
        /// Возвращает/устанавливает метод нахождения расстояний
        /// </summary>
        public DistanceMethod Method
        {
            get
            {
                return _method;
            }
            set
            {
                _method = value;
            }
        }

        /// <summary>
        /// Возвращает коэффициент E - максимально допустимое расстояние между экземпляром из выборки примеров и рассматриваемым экземпляром
        /// </summary>
        public double EMaximumDifference
        {
            get
            {
                return _EMaximumDifference;
            }
        }

        /// <summary>
        /// Устанавливает коэффициент E - максимально допустимое расстояние между экземпляром из выборки примеров и рассматриваемым экземпляром
        /// </summary>
        public void SetEMaximumDifference(double EMaximumDifference_)
        {
            if (EMaximumDifference_ < 0)
                throw new ArgumentOutOfRangeException("EmaximumDifference");
            _EMaximumDifference = EMaximumDifference_;
        }

        /// <summary>
        /// Устанавливает веса признаков для метода нахождения расстояний - взвешенный Эйлер
        /// </summary>
        public void SetAttributesWeights(int[] attributesWeights)
        {
            if (attributesWeights.Length != AttributesCount)
                throw new BIClassificationAttributesCountMismatch(AttributesCount, attributesWeights.Length, null);
            _attributesWeights = attributesWeights;
        }

        /// <summary>
        /// Устанавливает дополнительную информацию для каждого из признаков
        /// </summary>
        /// <param name="infos"></param>
        public override void SetInfos(double[] infos)
        {
            int[] weights = new int[infos.Length];
            for (int i = 0; i < infos.Length; i++)
            {
                weights[i] = Convert.ToInt32(Math.Truncate(infos[i]));
            }
            SetAttributesWeights(weights);            
        }

        /// <summary>
        /// Инициализирует классификатор
        /// </summary>
        /// <param name="classesCount">количество классов</param>
        /// <param name="attributesCount">количество признаков</param>                
        /// <param name="EMaximumDifference_">коэффициент E - максимально допустимое расстояние между экземпляром из выборки примеров и рассматриваемым экземпляром</param>
        public void Initialize(int classesCount, int attributesCount, double EMaximumDifference_)
        {
            SetInitialized(false);
            if (classesCount <= 0)
                throw new ArgumentOutOfRangeException("classesCount");
            if (attributesCount <= 0)
                throw new ArgumentOutOfRangeException("attributesCount");            
            if (EMaximumDifference_ < 0)
                throw new ArgumentOutOfRangeException("EmaximumDifference");            
            _EMaximumDifference = EMaximumDifference_;
            Examples = new List<ClassificationObject>[classesCount];
            RememberedExamples = new List<List<ClassificationObject>[]>();
            AttributesCount = attributesCount;
            _attributesWeights = new int[AttributesCount];
            for (int i = 0; i < AttributesCount; i++)
            {
                _attributesWeights[i] = 1;
            }
            SetAttributesWeights(_attributesWeights);
            LastRecognizedClassesMatching = new int[classesCount];
            for (int i = 0; i < LastRecognizedClassesMatching.Length; i++)
            {
                LastRecognizedClassesMatching[i] = 0;
            }
            _method = DistanceMethod.None;
            SetInitialized(true);
        }
        
        /// <summary>
        /// Классифицирует объект
        /// </summary>
        /// <param name="attributes"></param>
        protected override void OverloadedRecognizeClass(double[] attributes)
        {
            for (int classIndex = 0; classIndex < Examples.Length; classIndex++)
            {
                List<ClassificationObject> classExamples = Examples[classIndex];
                //запрещено теперь такоеif (classExamples == null) //нет примеров из этого класса
                //запрещено теперь такое    continue;
                for (int exampleIndex = 0; exampleIndex < classExamples.Count; exampleIndex++)
                {
                    ClassificationObject example = classExamples[exampleIndex];
                    double distance = _method == DistanceMethod.EstimatedEuclid ?
                        GeometricMethods.CalculateDistance(example.GetAttributes(), attributes, _attributesWeights)
                        :
                        GeometricMethods.CalculateDistance(example.GetAttributes(), attributes, _method);
                    if (distance < _EMaximumDifference)
                        LastRecognizedClassesMatching[classIndex] += 1;
                }
            }
        }                
    }
}
